package dmitrygusev.ratings.services;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;

import dmitrygusev.io.StringUtils;
import dmitrygusev.ratings.model.Movie;
import dmitrygusev.ratings.model.Rating;

public class KinopoiskSource extends AbstractSource {

	public static final Pattern KINOPOISK_AWAIT_RATING_PATTERN = Pattern.compile("await_percent\" class=\"perc\">([^<]*)<[^\\d+]*([^<]*)");
	public static final Pattern KINOPOISK_COMMENTS_COUNT_PATTERN = Pattern.compile("зрителей \\((\\d+)\\)");
	public static final Pattern KINOPOISK_COVER_PATTERN = Pattern.compile("(/images/film/[^\"]*)\"");
	public static final Pattern KINOPOISK_IMDB_RATING_PATTERN = Pattern.compile("IMDB:\\s+(\\d+(?:\\.\\d+)?)\\s+\\(([\\d\\s]+)\\)");
	public static final Pattern KINOPOISK_RATING_PATTERN = Pattern.compile("/level/83/film/.*>(.*)<span.*>(.*)</span>");
	public static final Pattern KINOPOISK_SUGGESTED_MOVIES_PATTERN = Pattern.compile("/level/1/film/(\\d+)/sr/1/.*>(.*)</a>.*/level/10/m_act\\[year\\]/(\\d+)[^(\\.\\.\\.)]*\\.\\.\\.\\s+([^<]*)<");
	public static final Pattern KINOPOISK_DETAILS_TITLE_PATTERN = Pattern.compile("class=\"moviename-big\">([^(<|&nbsp;)]*)");
	public static final Pattern KINOPOISK_DETAILS_TITLE2_PATTERN = Pattern.compile("color: #666; font-size: 13px\">([^<]*)");
	public static final Pattern KINOPOISK_DETAILS_YEAR_PATTERN = Pattern.compile("m_act%5Byear%5D/(\\d+)/");
	public static final Pattern KINOPOISK_DETAILS_CODE_PATTERN = Pattern.compile("/level/19/film/(\\d+)/");

	private void parseKinopoiskHTMLResponse(String html, String query, List<Movie> result) {
		Matcher m = KINOPOISK_SUGGESTED_MOVIES_PATTERN.matcher(html);
		
		Movie movie = new Movie();

		while (m.find()) {
			movie.setTitle(m.group(2));
			movie.setYear(m.group(3));
			movie.setCode(m.group(1));
			movie.setTitle2(m.group(4));
			movie.setUrl("http://www.kinopoisk.ru/level/1/film/" + movie.getCode());
			
			result.add(movie);
		} 
		
		if (result.isEmpty()) {
			tryParseMovieFromDetailsHtml(html, movie);
			
			if (! StringUtils.isNullOrEmpty(movie.getTitle())) {
				result.add(movie);
			}
		}
	}

	private void tryParseMovieFromDetailsHtml(String html, Movie movie) {
		Matcher m2 = KINOPOISK_DETAILS_TITLE_PATTERN.matcher(html);
		
		if (m2.find()) {
			movie.setTitle(m2.group(1));
		}

		Matcher m3 = KINOPOISK_DETAILS_TITLE2_PATTERN.matcher(html);
		
		if (m3.find()) {
			movie.setTitle2(m3.group(1));
		}

		Matcher m4 = KINOPOISK_DETAILS_YEAR_PATTERN.matcher(html);
		
		if (m4.find()) {
			movie.setYear(m4.group(1));
		}

		Matcher m5 = KINOPOISK_DETAILS_CODE_PATTERN.matcher(html);
		
		if (m5.find()) {
			movie.setCode(m5.group(1));
		}

		movie.setUrl("http://www.kinopoisk.ru/level/1/film/" + movie.getCode());
		
		parseRatingsFromDetailsHtml(html, movie);
	}

	@Override
	protected void fetchMovieDetails(Movie movie) {
		if (StringUtils.isNullOrEmpty(movie.getCommentsCount())) {
			String movieDetailsHtml = queryHttp(
					movie.getUrl(), "windows-1251", false);
		
			parseRatingsFromDetailsHtml(movieDetailsHtml, movie);
		}
	}

	private void parseRatingsFromDetailsHtml(String movieDetailsHtml, Movie movie) {
		if (movieDetailsHtml != null) {
			Matcher m2 = KINOPOISK_RATING_PATTERN.matcher(movieDetailsHtml);
			
			if (m2.find()) {
				Rating value = new Rating();

				value.setType("main");
				value.setValue(m2.group(1));
				value.setVotes(m2.group(2).replaceAll("&nbsp;", ""));
				
				movie.addRating(value);
			} else {
				logger.debug("Kinopoisk: No main rating found for movie " + movie.getCode());
			}
			
			Matcher m3 = KINOPOISK_AWAIT_RATING_PATTERN.matcher(movieDetailsHtml);
			if (m3.find()) {
				Rating value = new Rating();

				value.setType("await");
				value.setValue(m3.group(1));
				value.setVotes(m3.group(2));
				
				movie.addRating(value);
			} else {
				logger.debug("Kinopoisk: No await rating found for movie " + movie.getCode());
			}
			
			Matcher m4 = KINOPOISK_IMDB_RATING_PATTERN.matcher(movieDetailsHtml);
			if (m4.find()) {
				Rating value = new Rating();

				value.setType("IMDB");
				value.setValue(m4.group(1));
				value.setVotes(m4.group(2).replaceAll(" ", ""));
				
				movie.addRating(value);
			} else {
				logger.debug("Kinopoisk: No IMDB rating found for movie " + movie.getCode());
			}
			
			Matcher m5 = KINOPOISK_COMMENTS_COUNT_PATTERN.matcher(movieDetailsHtml);
			
			if (m5.find()) {
				movie.setCommentsCount(m5.group(1));
			} else {
				movie.setCommentsCount("0");
			}
			
			Matcher m6 = KINOPOISK_COVER_PATTERN.matcher(movieDetailsHtml);
			
			if (m6.find()) {
				movie.setCoverPicture("http://www.kinopoisk.ru" + m6.group(1));
			}
		}
	}

	private static String processKinopoiskLogin(String username, String password) {
		try {
			String msg = String.format("%1s=%2s&%3s=%4s&%5s=%6s", 
					URLEncoder.encode("shop_user[login]", "UTF-8"),
					URLEncoder.encode(username, "UTF-8"),
					URLEncoder.encode("shop_user[mem]", "UTF-8"),
					URLEncoder.encode("on", "UTF-8"),
					URLEncoder.encode("shop_user[pass]", "UTF-8"),
					URLEncoder.encode(password, "UTF-8")
					);
	
			URL url = new URL("http://www.kinopoisk.ru/level/30/");
	
			HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
			request.getFetchOptions().doNotFollowRedirects();
			request.setPayload(msg.getBytes());
	        request.addHeader(new HTTPHeader("Content-Type", "application/x-www-form-urlencoded"));
	        addFirefoxDefaultHeaders(request);
	        
	        HTTPResponse response = urlFetchService.fetch(request);
	        
	        for (HTTPHeader header : response.getHeaders()) {
				if (header.getName().equalsIgnoreCase("set-cookie")) {
					return header.getValue();
				}
			}
		} catch (Exception e) {
			logger.error("Error login to kinopoisk.ru: " + e);
		}
		return null;
	}

	@Override
	public void fetchRawMovies(String query, List<Movie> result) {
		try {
			this.cookie = processKinopoiskLogin(
					config.getProperty("kinopoisk-username"), 
					config.getProperty("kinopoisk-password"));
			
			String html = queryHttp(
					"http://www.kinopoisk.ru/index.php?kp_query="
							+ URLEncoder.encode(query, "windows-1251"), "windows-1251",
							true);
			
			if (html != null) {
				parseKinopoiskHTMLResponse(html, query, result);
			}
		}
		catch (UnsupportedEncodingException e) {
			logger.error(e);
		}
	}

	@Override
	public String getName() {
		return "КиноПоиск.Ru";
	}

}
